/*
 * OpenParts
 * A dynamic-loading components framework for GWT
 * Copyright (C) 2011 Christophe Bouthier  [chris{AT}binary-gastronome{DOT}fr]
 *
 * This work is partially based on work I have done at INRIA (http://www.inria.fr) 
 * in the context of the Qualipso European Project (http://qualipso.org/),
 * The work done at INRIA is Copyright (C) 2006-2011 INRIA
 *
 * This work is distributed under the LGPL version 3
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version 3
 * as published by the Free Software Foundation. See the GNU
 * Lesser General Public License in LGPL.txt for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package fr.openparts.OpenParts.client;


import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArrayString;

import fr.openparts.OpenParts.logging.client.OPLogger;
import fr.openparts.OpenParts.logging.client.OPLogging;

/**
 * The OPBindingService class is a binding object representing a binded service, containing {@link OPBindingResource}. OPBindingService is
 * a JavaScriptObject, that is a real javascript object mapped to Java thanks to GWT overlay absolutely fantastic technique.
 * 
 * @author Christophe Bouthier [chris{AT}binary-gastronome{DOT}fr]
 * @creation.date 31 December 2009
 */
public final class OPBindingService extends JavaScriptObject {
    
    protected static final OPLogger logger = OPLogging.getLoggerForDomain("binding");

    /**
     * Create and return a new OPBindingService object for the given service name.
     * 
     * @param serviceName
     *            the name of the service to bind. Shouldn't be null.
     * @return the corresponding OPBindingService. Cannot be null.
     */
    public final static OPBindingService serviceFactory(final String serviceName) {
        assert serviceName != null : "parameter serviceName shouldn't be null";

        return serviceFactoryImpl(serviceName);
    };

    /**
     * Real implementation of creating and returning a new OPBindingService object for the given service name.
     * 
     * @param serviceName
     *            the name of the service to bind.
     * @return the corresponding OPBindingService.
     */
    private final static native OPBindingService serviceFactoryImpl(final String serviceName) /*-{
        var service = {};
        service.name = serviceName;
        service.resources = {};
        return service;
    }-*/;

    /**
     * Protected constructor, necessary because it's a JavaScriptObject.
     */
    protected OPBindingService() {
    }

    /**
     * Return the name of the service represented by this binding object.
     * 
     * @return the name of the service represented by this binding object. Cannot be null.
     */
    public final native String getServiceName() /*-{
        return this.name;
    }-*/;

    /**
     * Returns an array of names of all resources binded for this service.
     * 
     * @return an array of names of all resources binded for this service.
     */
    public final String[] getResourcesNames() {
        final JsArrayString resourcesNames = getJsResourcesNames();
        String[] result = new String[resourcesNames.length()];
        for (int i = 0; i < resourcesNames.length(); i++) {
            result[i] = resourcesNames.get(i);
        }
        return result;
    }

    /**
     * Returns a native js array containing all resources names.
     * 
     * @return a native js array containing all resources names. Can be empty, cannot be null.
     */
    private final native JsArrayString getJsResourcesNames() /*-{
        var names = new Array();
        var count = 0;

        for (var p in this.resources) {
            if (this.resources.hasOwnProperty(p)) {
                names[count] = p;
                ++count;
            }
        }

        return names;
    }-*/;
    
    /**
     * Return the {@link OPBindingResource} object corresponding to the given resource binded in this service. Return null if there is no such resource
     * binded.
     * 
     * @param resourceName
     *            the name of the searched binded resource. Shouldn't be null.
     * @return the {@link OPBindingResource} corresponding to this resource, or null if there is no such resource binded.
     */
    public final OPBindingResource getResource(final String resourceName) {
        assert resourceName != null : "parameter resourceName shouldn't be null";

        return getResourceImpl(resourceName);
    }

    /**
     * Real implementation of returning the {@link OPBindingResource} object corresponding to the given resource binded in this service. Return null if
     * there is no such resource binded.
     * 
     * @param resourceName
     *            the name of the searched registered resource.
     * @return the {@link OPBindingResource} corresponding to this resource, or null if there is no such resource registered.
     */
    private final native OPBindingResource getResourceImpl(final String resourceName) /*-{
        if (! this.resources[resourceName]) {
           return null;
        }

        return this.resources[resourceName];
    }-*/;

    /**
     * Bind the given {@link OPBindingResource} object as resource for this service.  If a resource with the same name was already binded, just change its value.
     * 
     * @param resource
     *            the resource to bind. Shouldn't be null.
     */
    public final void bindResource(final OPBindingResource resource) {
        assert resource != null : "parameter resource shouldn't be null";
        logger.log("Binding resource " + resource.getResourceName() + " for service " + getServiceName());

        bindResourceImpl(resource);
    }

    /**
     * Real implementation for binding the given {@link OPBindingResource} object as resource for this service.
     * 
     * @param resource
     *            the resource to bind.
     */
    private final native void bindResourceImpl(final OPBindingResource resource) /*-{
        this.resources[resource.name] = resource;
    }-*/;

}
